import { IState } from "./interface/IState";
import { IStateMachine } from "./interface/IStateMachine";
import { ITransition } from "./interface/ITransition";
import { State } from "./state/State";
import { StateBase } from "./state/StateBase";
import { Transition } from "./transition/Transition";
import { TransitionBase } from "./transition/TransitionBase";
/**
 * 状态特殊信息
 */
class StateBundle {
    /**
     * 状态
     */
    state: IState
    /**
     * 当前状态对应的转换信息
     */
    transitions: Array<ITransition>

    /**
     * 本状态对应的事件转换
     */
    triggerToTransitions: Map<string, Array<ITransition>>

    /**
     * 添加一个转换
     * @param t - 转换信息
     */
    addTransition(t: ITransition) {
        if (!this.transitions) this.transitions = []
        if (this.transitions.indexOf(t) == -1)
            this.transitions.push(t)
    }

    /**
     * 添加一个事件类型的转换
     * @param event - 事件
     * @param t - 转换信息
     */
    addTriggerTransition(event: string, t: ITransition) {
        if (!this.triggerToTransitions) this.triggerToTransitions = new Map<string, Array<ITransition>>()
        let arr: Array<ITransition>
        if (this.triggerToTransitions.has(event)) {
            arr = this.triggerToTransitions.get(event)
        } else {
            arr = []
            this.triggerToTransitions.set(event, arr)
        }
        if (arr.indexOf(t) == -1) arr.push(t)
    }
}
/**
 * 状态机实现类
 */
export class StateMachine extends StateBase implements IStateMachine {
    isStateMachine = true
    /**
     * 起始状态
     */
    protected startState: number = -1

    /**
     * 当前状态
     */
    protected mCurState: IState

    /**
     * 当前状态的包装信息
     */
    protected curBundle: StateBundle
    /**
     * 准备要进入的状态
     */
    private pendingState: { id: number, data?: any } = { id: -1, data: null }

    /**
     * 任意动画转换信息
     */
    private transitionsFromAny: Array<ITransition> = []

    /**
     * 状态Bundle
     */
    private stateBundles: Map<number, StateBundle> = new Map<number, StateBundle>()

    /**
     * 消息对应的任意转换
     */
    private triggerTransitionsFromAny: Map<string, Array<ITransition>> = new Map<string, Array<ITransition>>()

    /**
     * 是否是根状态
     */
    private get isRootFsm() {
        return this.fsm == null
    }

    get activeState(): IState {
        return this.mCurState
    }

    get activeID(): number {
        return this.activeState ? this.activeState.stateId : -1
    }

    constructor() {
        super()
    }

    /**
     * 设置开始状态
     * @param id - 开始状态id
     */
    setStartState(id: number) {
        this.startState = id
    }


    stateCanExit(): void {
        if (this.pendingState.id != -1) {
            this.changeState(this.pendingState.id, this.pendingState.data)
            this.pendingState.id = -1
            this.pendingState.data = null
        } else {
            // this.onExit(null, null)//不能停止当前状态
        }
        if (this.fsm) this.fsm.stateCanExit()
    }


    requestExit() {
        let realState = this.getRealActiveState()
        if (realState && realState.needExitTime)
            realState.requestExit()
        else
            this.fsm.stateCanExit()
    }

    getRealActiveState(){
        if(!this.activeState){
            return null
        }
        let nowState = this.activeState
        while(true){
            if(nowState && nowState.isStateMachine){
                nowState = (nowState as StateMachine).activeState
            }else{
                break
            }
        }
        if(nowState){
            return nowState
        }else{
            return null
        }
    }

    curStateNeedExitTime(){
        let realState = this.getRealActiveState()
        if(realState){
            return realState.needExitTime
        }else{
            return false
        }
    }

    requestStateChange(id: number, param: number, forceInstantly: boolean) {
        if (!this.activeState || !this.curStateNeedExitTime() || forceInstantly) {
            this.changeState(id, param)//立即切换状态
        } else {
            //准备下一次切换状态自动切换到这个状态
            this.pendingState.id = id
            this.pendingState.data = param
            if (this.activeState != null) {
                this.activeState.requestExit()
            }
        }
    }
    
    stop() {
        if (this.pendingState.id != -1) {
            this.pendingState.id = -1
            this.pendingState.data = null
        }
        if (this.mCurState != null) {
            this.mCurState.onExit(null)
            this.mCurState = null
            this.curBundle = null
        }
    }

    init() {
        if (this.isRootFsm) this.onEnter(null)
    }

    /**
     * 进入状态
     * @param prevState - 上一个状态 
     * @param param - 状态数据
     */
    onEnter(prevState: IState, param?: any): void {
        this.changeState(this.startState, param)
        for (let index = 0; index < this.transitionsFromAny.length; index++) {
            const t = this.transitionsFromAny[index];
            t.onEnter()
        }
        this.triggerTransitionsFromAny.forEach(element => {
            element.forEach(v => {
                v.onEnter()
            })
        });
        // super.onEnter(prevState,param)
    }

    /**
     * 离开状态
     * @param nextState - 下一个状态 
     * @param param - 数据
     */
    onExit(nextState: IState, param?: any): void {
        if (this.activeState != null) {
            this.mCurState.onExit(null, param)
            this.mCurState = null
            this.curBundle = null
        }
    }

    /**
     * 状态持续函数
     * @param dt - 时间间隔
     */
    onUpdate(dt) {
        if (this.activeState) {
            for (let index = 0; index < this.transitionsFromAny.length; index++) {
                const t = this.transitionsFromAny[index];
                if (t.to == this.activeState.stateId) continue
                if (this.tryTransition(t)) break
            }
            if (this.mCurState && this.curBundle) {
                if (this.curBundle.transitions) {
                    for (let index = 0; index < this.curBundle.transitions.length; index++) {
                        const t = this.curBundle.transitions[index];
                        if (this.tryTransition(t)) break
                    }
                }
                if (this.mCurState)//这里有可能发生状态切换
                    this.mCurState.onUpdate(dt)
            }
        }
    }

    /**
     * 添加一个状态到状态机
     * @param id - 状态id
     * @param state - 状态
     */
    addState(id: number, state: IState) {
        state.fsm = this
        state.stateId = id
        state.init()

        let bundle = this.getOrCreateStateBundle(id)
        bundle.state = state
        if (this.startState == -1) this.setStartState(id)
        return this
    }

    /**
     * 添加一个状态到状态机
     * @param stateId - 状态id
     * @param onEnter - 状态进入
     * @param onUpdate - 状态执行
     * @param onExit - 状态退出
     * @param canExit - 状态是否能退出
     * @param needExitTime - 状态是否需要等待退出
     * @returns 
     */
    addStateFunc(
        stateId: number,
        onEnter: (state: State, prevState: IState, param?: any) => void = null,
        onUpdate: (state: State, dt: number) => void = null,
        onExit: (state: State, nextState: IState, param?: any) => void = null,
        canExit: (state: State) => boolean = null,
        needExitTime = false) {
        if (onEnter == null &&
            onUpdate == null &&
            onExit == null &&
            canExit == null) {
            this.addState(stateId, new StateBase())
        } else {
            this.addState(stateId, new State(onEnter, onUpdate, onExit, canExit, needExitTime))
        }
        return this
    }



    /**
     * 添加跳转
     * @param from - 
     * @param to - 
     * @param condition - 条件 
     * @param forceInstantly - 是否强制 
     * @param data - 数据
     */
    addTransition(from: number, to: number, condition?: (ITransition) => boolean, forceInstantly?: boolean, data?: any)
    /**
     * 添加跳转
     * @param t - 跳转信息
     */
    addTransition(t: ITransition)
    addTransition() {
        let addTran = (t: ITransition) => {
            this.initTransition(t)
            let bundle = this.getOrCreateStateBundle(t.from)
            bundle.addTransition(t)
        }
        if (arguments.length == 1) {
            addTran(arguments[0])   
        } else {
            let from = arguments[0]
            let to = arguments[1]
            let condition = arguments.length > 2 ? arguments[2] : null
            let forceInstantly = arguments.length > 3 ? arguments[3] : false
            let data = arguments.length > 4 ? arguments[4] : null
            addTran(this.createOptimizedTransition(from, to, condition, forceInstantly, data))
        }

        return this
    }

    /**
     * 添加任意状态跳转
     * @param t - 跳转信息
     */
    addTransitionFromAny(t: ITransition);
    /**
     * 添加一个任意类型的跳转
     * @param to - 目标
     * @param condition - 条件
     * @param forceInstantly - 是否强制 
     * @param data - 数据 
     * @returns 
     */
    addTransitionFromAny(to: number, condition?: (ITransition) => boolean, forceInstantly?: boolean, data?: any)
    addTransitionFromAny(...args: any[]) {
        let t: ITransition
        if (typeof (args[0]) == "number") {
            let to: number = args[0]
            let condition: (ITransition) => boolean = args.length > 1 ? args[1] : null
            let forceInstantly = args.length > 2 ? args[2] : false
            let data = args.length > 3 ? args[3] : null
            t = this.createOptimizedTransition(-1, to, condition, forceInstantly, data)
        } else {
            t = args[0]
        }
        this.initTransition(t)
        if (this.transitionsFromAny.indexOf(t) == -1) this.transitionsFromAny.push(t)
        return this
    }



    /**
     * 添加事件类型的转换
     * @param event - 事件
     * @param t - 转换信息
     * @returns 
     */
    addTriggerTransition(event: string, t: ITransition)
    /**
     * 添加事件类型的转换
     * @param event - 事件
     * @param from - 
     * @param to - 目标
     * @param condition - 条件 
     * @param forceInstantly - 是否立即执行 
     * @param data -  附加数据 
     */
    addTriggerTransition(event: string, from: number, to: number, condition?: (ITransition) => boolean, forceInstantly?: boolean, data?: any)
    addTriggerTransition(...args: any[]) {
        let event = args[0]
        let t: ITransition
        if (typeof (args[1]) == "number") {
            let from: number = args[1]
            let to: number = args[2]
            let condition = args.length > 3 ? args[3] : null
            let forceInstantly = args.length > 4 ? args[4] : false
            let data = args.length > 5 ? args[5] : false
            t = this.createOptimizedTransition(from, to, condition, forceInstantly, data)
        } else {
            t = args[1]
        }
        this.initTransition(t)
        let bundle = this.getOrCreateStateBundle(t.from)
        bundle.addTriggerTransition(event, t)
        return this
    }

    /**
     * 添加任意事件类型的转换
     * @param event - 事件类型
     * @param t - 转换信息
     * @param data - 附加数据
     */
    addTriggerTransitionFromAny(event: string, t: ITransition)
    addTriggerTransitionFromAny(event: string, to: number, condition?: (ITransition) => boolean, forceInstantly?: boolean, data?: any)
    addTriggerTransitionFromAny(...args: any[]) {
        let event = args[0]
        let t: ITransition
        if (typeof (args[1]) == "number") {
            let to: number = args[1]
            let condition = args.length > 2 ? args[2] : null
            let forceInstantly = args.length > 3 ? args[3] : false
            t = this.createOptimizedTransition(-1, to, condition, forceInstantly)
        } else {
            t = args[1]
        }
        this.initTransition(t)
        let transitionsOfTrigger: Array<ITransition>
        if (this.triggerTransitionsFromAny.has(event)) transitionsOfTrigger = this.triggerTransitionsFromAny.get(event)
        else {
            transitionsOfTrigger = []
            this.triggerTransitionsFromAny.set(event, transitionsOfTrigger)
        }
        transitionsOfTrigger.push(t)
    }

    /**
     * 触发事件类型的状态转换
     * @param event - 事件类型
     * @param data - 事件数据
     */
    public trigger(event: string,data?:any): boolean {
        let ret = false;//是否已经跳转
        // let triggerTransitions:Array<ITransition>;
        if (this.triggerTransitionsFromAny.has(event)) {
            let triggerTransitions = this.triggerTransitionsFromAny.get(event)
            for (let index = 0; index < triggerTransitions.length; index++) {
                const transition = triggerTransitions[index];
                if (this.activeID != -1 && transition.to == this.activeID) continue
                if (this.tryTransition(transition,data)) {
                    ret = true
                    break
                }
            }
        }
        if (!ret) {
            if (this.activeState) {
                let bundle = this.stateBundles.get(this.activeState.stateId)
                if (bundle && bundle.triggerToTransitions && bundle.triggerToTransitions.has(event)) {
                    let ts = bundle.triggerToTransitions.get(event)
                    for (let index = 0; index < ts.length; index++) {
                        const t = ts[index];
                        ret = this.tryTransition(t,data)
                        if (ret) break
                    }
                }
            }
        }

        if (!ret && this.activeState) {
            if (this.activeState["trigger"]) {
                this.activeState["trigger"](event)
            }
        }
        return ret
    }

    /**
     * 改变状态
     * @param id - 状态id
     * @param param - 状态跳转参数
     */
    public changeState(id: number, param: any = null) {
        let nextStateBundle = this.stateBundles.has(id) ? this.stateBundles.get(id) : null
        if (!nextStateBundle) console.warn(`需要跳转到的状态:[${id}]不存在,维持现在状态不变`)
        else {
            let last = this.activeState
            if (last) last.onExit(nextStateBundle.state, param)
            this.mCurState = nextStateBundle.state
            this.mCurState.onEnter(last, param)
            this.curBundle = nextStateBundle
            if (nextStateBundle.transitions) {
                for (let index = 0; index < nextStateBundle.transitions.length; index++) {
                    const t = nextStateBundle.transitions[index];
                    t.onEnter()
                }
            }
            if (nextStateBundle.triggerToTransitions) {
                nextStateBundle.triggerToTransitions.forEach(values => {
                    values.forEach((t) => {
                        t.onEnter()
                    })
                });
            }
        }
        // if(this.activeState) this.activeState
    }

    private initTransition(t: ITransition) {
        t.fsm = this
        t.init()
    }

    private getOrCreateStateBundle(stateId: number) {
        let bundle: StateBundle
        if (this.stateBundles.has(stateId)) bundle = this.stateBundles.get(stateId)
        else {
            bundle = new StateBundle()
            this.stateBundles.set(stateId, bundle)
        }
        return bundle
    }

    private createOptimizedTransition(
        from: number,
        to: number,
        condition: (t: ITransition) => boolean = null,
        forceInstantly: boolean = false,
        data: any = null) {
        if (condition) {
            return new Transition(from, to, condition, forceInstantly, data)
        } else {
            return new TransitionBase(from, to, forceInstantly, data)
        }
    }

    private tryTransition(t: ITransition,data?:any): boolean {
        let flag = t.shouldTransition()
        if (flag) {
            this.requestStateChange(t.to, data || t.data, t.forceInstantly)
        }
        return flag
    }
}